<!doctype html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,viewport-fit=cover"><base href=https://www.lowrisc.org><link rel=icon type=image/png sizes=32x32 href=/favicon.png><title>A debug session &middot; lowRISC: Collaborative open silicon engineering</title><link href=/main.21c9d.css rel=stylesheet><script type=application/javascript>var doNotTrack=false;if(!doNotTrack){(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');ga('create','UA-53520714-1','auto');ga('send','pageview');}</script></head><body><header><nav class="navbar navbar-expand-md navbar-light"><div class=container><a class=navbar-brand href=#><img src=/img/logo/logo-dualcolor.svg alt=lowRISC></a>
<button class=navbar-toggler type=button data-toggle=collapse data-target=#navbarCollapse aria-controls=navbarCollapse aria-expanded=false aria-label="Toggle navigation">
<span class=navbar-toggler-icon></span></button><div class="collapse navbar-collapse" id=navbarCollapse><ul class="navbar-nav ml-auto"><li class=nav-item><a href=/our-work class=nav-link>Our work</a></li><li class=nav-item><a href=/open-silicon class=nav-link>Open Silicon</a></li><li class=nav-item><a href=/community class=nav-link>Community</a></li><li class=nav-item><a href=/blog class=nav-link>Blog</a></li><li class=nav-item><a href=/jobs class=nav-link>Jobs</a></li><li class=nav-item><a href=/about class=nav-link>About us</a></li><li class=nav-item><a class="btn lr-navbar-btn-gh" href=https://github.com/lowrisc>GitHub</a></li></ul></div></div></nav></header><main role=main><div class=container><h1>A debug session</h1><p>Once you have an Open SoC Debug daemon running and it connected to a
target system, you can initiate a debug session. In the following you will
learn the different basic features to interact with the system.</p><p>Before you begin, you should build the example programs:</p><pre><code>make -C $TOP/fpga/bare_metal/examples hello.riscv trace.riscv
</code></pre><h3 id=resetting-the-system:da9d471c4fb90641fea894c4e666b9c3>Resetting the system</h3><p>In the first step we will start the command line interface and reset
the system. When starting the command line interface it automatically
connects to the daemon.</p><p>The steps to connect to the verilated RTL simulation are based on
what you learned before:</p><ul><li><p>Start simulation with preloaded software (terminal 1):</p><pre><code>$TOP/vsim/DebugConfig-sim +load=$TOP/fpga/bare_metal/examples/hello.riscv +waitdebug
</code></pre></li><li><p>Connect the daemon (terminal 2):</p><pre><code>opensocdebugd tcp
</code></pre></li><li><p>Start the command line interface (terminal 3):</p><pre><code>osd-cli
</code></pre></li></ul><p>Now you are connected to the debug system and can inspect and control
the system. First, reset the system (<code>osd&gt;</code> is the command line prompt
in the following) and leave the command line again:</p><pre><code>osd&gt; reset
osd&gt; quit
</code></pre><p>The command has reset the entire system. If you run the simulation
with vcd output (add <code>+vcd</code> to the parameters) you can see how it is
reset. <code>reset</code> asserts both reset signals for CPU and the rest of the SoC.
With the parameter <code>-halt</code>, the CPU reset remains asserted to allow loading
programs to memory or configuring debug modules.
Running <code>start</code> de-asserts the CPU reset then:</p><pre><code>osd&gt; reset -halt
osd&gt; [.. some other commands ..]
osd&gt; start
</code></pre><p>So now we want to see that in action.</p><h3 id=capturing-uart-output:da9d471c4fb90641fea894c4e666b9c3>Capturing UART output</h3><p>As described before, the UART controller has been replaced with an
emulation module that has the same physical interface, but transports
the characters over the debug infrastructure.</p><p>In the command line you can start a terminal that displays the output
by executing <code>terminal &lt;module id&gt;</code>. You need to set the module id as
enumerated (here: <code>2</code>). This will open an <code>xterm</code> and direct output to
it.</p><p>So when running the following sequence with the terminal open, you
will see that the cores start and emit the <code>printf</code> message after the
third command and after the fourth again:</p><pre><code>osd&gt; reset -halt
osd&gt; terminal 2
osd&gt; start
osd&gt; reset
</code></pre><p>Now you can run the programs as before and reset the system
without touching a board, but the real power comes with the other
modules.</p><h3 id=generate-a-function-trace:da9d471c4fb90641fea894c4e666b9c3>Generate a function trace</h3><p>To analyze the execution of the program on the core, you can use the
core trace module. It can be configured to log all function calls and
returns from functions to a file. It also logs changes of the
execution mode.</p><p>A function trace is generated by activating the core trace module and
giving the ELF currently executed on the core.</p><pre><code>osd&gt; reset -halt
osd&gt; terminal 2
osd&gt; ctm log ctm.log 4 path/to/hello.riscv
osd&gt; start
osd&gt; quit
</code></pre><p>Now you can inspect the output file <code>ctm.log</code>:</p><pre><code>00033c7c enter init_tls
00033cac enter memcpy
00033d48 enter memset
00033dd1 enter thread_entry
00033e41 enter main
00033e67 enter uart_init
00033f21 enter printf
00033f9e enter vprintfmt
0003432e enter syscall
000343a5 change mode to 3
0003444c enter handle_trap
00034616 enter handle_frontend_syscall
0003469b enter uart_send
000346f4 enter uart_send
00034723 enter uart_send
00034752 enter uart_send
00034781 enter uart_send
000347b0 enter uart_send
000347df enter uart_send
0003480e enter uart_send
0003483d enter uart_send
0003486c enter uart_send
0003489b enter uart_send
000348ca enter uart_send
000348f9 enter uart_send
00034b9f enter exit
00034bcd enter syscall
00034bf4 change mode to 3
00034c4f enter handle_trap
00034c92 enter tohost_exit
Overflow, missed 12 events
Overflow, missed 13 events
Overflow, missed 13 events
Overflow, missed 13 events
Overflow, missed 13 events
Overflow, missed 13 events
Overflow, missed 14 events
Overflow, missed 14 events
Overflow, missed 3 events
Overflow, missed 3 events
Overflow, missed 3 events
Overflow, missed 4 events
Overflow, missed 3 events
Overflow, missed 3 events
Overflow, missed 3 events
Overflow, missed 3 events
Overflow, missed 4 events
Overflow, missed 4 events
Overflow, missed 4 events
[..]
</code></pre><h3 id=minimally-invasive-software-debugging:da9d471c4fb90641fea894c4e666b9c3>Minimally-invasive software debugging</h3><p>As introduced, lowRISC adds a software trace debugging technique that
is minimally invasive by only adding a few cycles each time it is called
without stalling the system.</p><p>The code <code>trace.c</code> from the examples emits four of such events:</p><pre><code>#include &lt;stdint.h&gt;

#define STM_TRACE(id, value) \
  { \
    asm volatile (&quot;mv   a0,%0&quot;: :&quot;r&quot; ((uint64_t)value) : &quot;a0&quot;); \
    asm volatile (&quot;csrw 0x8f0, %0&quot; :: &quot;r&quot;(id)); \
  }

static void trace_event0() {
  STM_TRACE(0, 0x42);
}

static void trace_event1(uint64_t value) {
  STM_TRACE(1, value);
}

static void trace_event2(uint64_t id, uint64_t value) {
  STM_TRACE(id, value);
}

int main() {
  STM_TRACE(0x1234, 0xdeadbeef);

  trace_event0();
  trace_event1(23);
  trace_event2(0xabcd, 0x0123456789abcdef);
}
</code></pre><p>You need to load it with the simulation:</p><pre><code>$TOP/vsim/DebugConfig-sim +waitdebug +load=$TOP/fpga/bare_metal/examples/trace.riscv
</code></pre><p>Then open the CLI and run:</p><pre><code>osd&gt; reset -halt
osd&gt; stm log stm.log 5
osd&gt; start
osd&gt; quit
</code></pre><p>You need to wait a few seconds between start and quit. This will
generate in <code>stm.log</code>:</p><pre><code>0002574e 1234 00000000deadbeef
00025784 0000 0000000000000042
0002579c 0001 0000000000000017
000257d2 abcd 0123456789abcdef
</code></pre><h3 id=scripting-the-command-line-interface:da9d471c4fb90641fea894c4e666b9c3>Scripting the command line interface</h3><p>You can execute commands at startup of the CLI by creating a script,
e.g. this <code>startup.script</code>:</p><pre><code>reset -halt
terminal 2
</code></pre><p>By calling the CLI with this as a startup script the commands are
executed before you enter the shell:</p><pre><code>osd-cli -s startup.script
execute: reset -halt
execute: terminal 2
osd&gt; 
</code></pre><p>Similarly, you can run CLI in batch mode, e.g., with <code>run.script</code>:</p><pre><code>reset -halt
stm log stm.log 5
ctm log ctm.log 4 path/to/hello.riscv
start
wait 10
quit
</code></pre><p>Calling the CLI in batch mode will run the demos from above and wait
for 10 seconds before quitting:</p><pre><code>osd-cli -b run.script
execute: reset -halt
execute: stm log stm.log 5
execute: ctm log ctm.log 4 
execute: start
execute: wait 10
execute: quit
</code></pre><h3 id=loading-the-elf-to-memory:da9d471c4fb90641fea894c4e666b9c3>Loading the ELF to memory</h3><p>Until now we have started the simulation with the ELF program
pre-loaded into the memory. We can also load the ELF into the memory
in the CLI:</p><pre><code>osd-cli
osd&gt; mem loadelf path/to/hello.riscv 3
osd&gt; terminal 2
osd&gt; start
</code></pre><h3 id=python-scripting:da9d471c4fb90641fea894c4e666b9c3>Python scripting</h3><p>To make the debugging really convenient we have python bindings, so
that you can run automated debug sessions with re-usable scripts. An
example script is given as
<code>$TOP/tools/share/opensocdebug/examples/runelf.py</code>:</p><pre><code>import opensocdebug 
import sys

if len(sys.argv) &lt; 2:
    print &quot;Usage: runelf.py &lt;filename&gt;&quot;
    exit(1)

elffile = sys.argv[1]

osd = opensocdebug.Session()

osd.reset(halt=True)

for m in osd.get_modules(&quot;STM&quot;):
    m.log(&quot;stm{:03x}.log&quot;.format(m.get_id()))

for m in osd.get_modules(&quot;CTM&quot;):
    m.log(&quot;ctm{:03x}.log&quot;.format(m.get_id()), elffile)

for m in osd.get_modules(&quot;MAM&quot;):
    m.loadelf(elffile)

osd.start()
osd.wait(10)
</code></pre><p>You can see that it does pretty much what we did here in the
tutorial. However it is more flexible as it works with other systems too
and is more powerful when integrated with your testing environment.</p><p>To try this example, just run:</p><pre><code>python $TOP/tools/share/opensocdebug/examples/runelf.py path/to/hello.riscv
</code></pre></div></main><footer class=lr-footer><div class=container><div class=row><div class="col-lg-2 d-none d-lg-block"><img src=/img/logo/logo-dualcolor.svg width=150px></div><div class=col><p>The text content on this website is licensed under a <a href=https://creativecommons.org/licenses/by/4.0/>Creative Commons Attribution 4.0 International License</a>, except where otherwise noted. No license is granted for logos or other trademarks. Other content &copy; lowRISC Contributors.</p><a href=/privacy-policy>Privacy and cookies policy</a>
&middot; <a href=/usage-licence>Usage licence</a></p></div><div class=col-lg-2><p><a href=#>Back to top</a></p></div></div></div></footer><script src=/main.21c9d.js></script></body></html>